#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
SIMION 参数设置窗口

提供图形化界面设置 SIMION 参数，并使用 params_store 进行全局状态管理
"""

import tkinter as tk
from tkinter import ttk, filedialog, messagebox
import os

# 导入ttkbootstrap美化
try:
    import ttkbootstrap as ttk
    from ttkbootstrap.constants import *
    from ttkbootstrap.style import Style
    from ttkbootstrap.icons import Icon
    import base64
    from io import BytesIO
    BOOTSTRAP_AVAILABLE = True
except ImportError:
    BOOTSTRAP_AVAILABLE = False

# 导入配置（自动配置Python路径）
from settings import BASE_DIR, FONT_FAMILY, FONT_SIZE_NORMAL, FONT_SIZE_BOLD, COLORS, WINDOW_BG_COLOR
from utils.window_utils import calculate_window_size, center_window
from utils.logger import logger
from store import params_store
from utils.icon_utils import get_icon_info, get_icon_error


class SettingsWindow:
    """参数设置窗口"""
    
    def __init__(self, parent=None):
        """
        初始化参数设置窗口
        
        参数:
            parent: 父窗口（可选）
        """
        # 创建窗口
        if parent:
            if BOOTSTRAP_AVAILABLE:
                # 使用父窗口的样式
                self.window = tk.Toplevel(parent)
                # 尝试获取父窗口的样式
                if hasattr(parent, 'style'):
                    self.style = parent.style
                else:
                    self.style = Style(theme='cosmo')
            else:
                self.window = tk.Toplevel(parent)
        else:
            if BOOTSTRAP_AVAILABLE:
                self.style = Style(theme='cosmo')
                self.window = self.style.master
            else:
                self.window = tk.Tk()
        
        if BOOTSTRAP_AVAILABLE:
            self.window.title("SIMION 参数设置")
        else:
            self.window.title("SIMION 参数设置")
        
        # 设置窗口大小和位置（使用更大的尺寸）
        width, height = calculate_window_size(
            width_percent=0.45,
            height_percent=0.80,
            min_width=550,
            max_width=700,
            min_height=750,
            max_height=950
        )
        center_window(self.window, width, height)
        self.window.resizable(True, True)
        self.window.minsize(550, 750)
        
        # 设置窗口样式
        self.window.configure(bg=WINDOW_BG_COLOR)
        
        # 从 params_store 加载当前值
        self.load_from_store()
        
        # 创建界面
        self.create_widgets()
        
        # 绑定关闭事件
        self.window.protocol("WM_DELETE_WINDOW", self.on_cancel)
        
        # 存储Canvas引用以便在关闭时清理事件绑定
        self.main_canvas = None
    
    def load_from_store(self):
        """从 params_store 加载参数"""
        self.simion_path_var = tk.StringVar(value=params_store.get('simion_path', ''))
        self.iob_path_var = tk.StringVar(value=params_store.get('iob_path', ''))
        self.lua_path_var = tk.StringVar(value=params_store.get('lua_path', ''))
        self.csv_save_path_var = tk.StringVar(value=params_store.get('csv_save_path', ''))
        self.rfvolt_var = tk.DoubleVar(value=params_store.get('_rfvolt', 5000.0))
        self.rfvolt2_var = tk.DoubleVar(value=params_store.get('_rfvolt2', 2000.0))
        self.freq_start_var = tk.StringVar(value=str(params_store.get('_frequency_start') or ''))
        self.freq_end_var = tk.StringVar(value=str(params_store.get('_frequency_end') or ''))
        self.freq_step_var = tk.IntVar(value=params_store.get('_frequency_step', 10000))
        self.ke_var = tk.DoubleVar(value=params_store.get('_ke', 90000.0))
        self.length_var = tk.DoubleVar(value=params_store.get('_length', 500.0))
        self.thread_count_var = tk.IntVar(value=params_store.get('thread_count', 2))
        
        # 外观设置变量
        self.appearance_theme_var = tk.StringVar(value=params_store.get('appearance_theme', 'cosmo'))
        self.appearance_font_size_var = tk.IntVar(value=params_store.get('appearance_font_size', 9))
        self.appearance_window_width_var = tk.IntVar(value=params_store.get('appearance_window_width', 1200))
        self.appearance_window_height_var = tk.IntVar(value=params_store.get('appearance_window_height', 800))
        self.appearance_window_resizable_var = tk.BooleanVar(value=params_store.get('appearance_window_resizable', True))
        self.appearance_window_remember_size_var = tk.BooleanVar(value=params_store.get('appearance_window_remember_size', True))
    
    def create_widgets(self):
        """创建界面组件"""
        # 创建底部按钮区域（先创建，固定在底部）
        self.create_button_section()
        
        # 主框架容器
        if BOOTSTRAP_AVAILABLE:
            main_container = ttk.Frame(self.window)
        else:
            main_container = tk.Frame(self.window, bg=WINDOW_BG_COLOR)
        main_container.pack(fill=tk.BOTH, expand=True, padx=0, pady=0)
        
        # 主框架（带滚动条）
        if BOOTSTRAP_AVAILABLE:
            main_canvas = tk.Canvas(main_container, highlightthickness=0)
            scrollbar = ttk.Scrollbar(main_container, orient="vertical", command=main_canvas.yview, bootstyle=SECONDARY)
            scrollable_frame = tk.Frame(main_canvas)
        else:
            main_canvas = tk.Canvas(main_container, bg=WINDOW_BG_COLOR, highlightthickness=0)
            scrollbar = ttk.Scrollbar(main_container, orient="vertical", command=main_canvas.yview)
            scrollable_frame = tk.Frame(main_canvas, bg=WINDOW_BG_COLOR)
        
        scrollable_frame.bind(
            "<Configure>",
            lambda e: main_canvas.configure(scrollregion=main_canvas.bbox("all"))
        )
        
        main_canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")
        main_canvas.configure(yscrollcommand=scrollbar.set)
        
        # 绑定鼠标滚轮事件
        def _on_mousewheel(event):
            main_canvas.yview_scroll(int(-1*(event.delta/120)), "units")
        
        def _bind_to_mousewheel(event):
            main_canvas.bind_all("<MouseWheel>", _on_mousewheel)
        
        def _unbind_from_mousewheel(event):
            main_canvas.unbind_all("<MouseWheel>")
        
        # 当鼠标进入Canvas时绑定滚轮事件，离开时解绑
        main_canvas.bind('<Enter>', _bind_to_mousewheel)
        main_canvas.bind('<Leave>', _unbind_from_mousewheel)
        
        # 存储Canvas引用
        self.main_canvas = main_canvas
        
        main_canvas.pack(side="left", fill="both", expand=True, padx=(20, 0), pady=(20, 10))
        scrollbar.pack(side="right", fill="y", pady=(20, 10))
        
        # 标题
        if BOOTSTRAP_AVAILABLE:
            title_label = ttk.Label(
                scrollable_frame,
                text="⚙️ SIMION 参数配置",
                font=('Arial', 16, 'bold'),
                bootstyle=PRIMARY
            )
        else:
            title_label = tk.Label(
                scrollable_frame,
                text="SIMION 参数配置",
                font=(FONT_FAMILY, FONT_SIZE_BOLD + 2, "bold"),
                bg=WINDOW_BG_COLOR,
                fg=COLORS['primary']
            )
        title_label.pack(pady=(0, 20))
        
        # SIMION 路径设置
        self.create_simion_path_section(scrollable_frame)
        
        # 分隔线
        ttk.Separator(scrollable_frame, orient='horizontal').pack(fill=tk.X, pady=15)
        
        # RF 电压参数设置
        self.create_rf_params_section(scrollable_frame)
        
        # 分隔线
        ttk.Separator(scrollable_frame, orient='horizontal').pack(fill=tk.X, pady=15)
        
        # 频率参数设置
        self.create_frequency_section(scrollable_frame)
        
        # 分隔线
        ttk.Separator(scrollable_frame, orient='horizontal').pack(fill=tk.X, pady=15)
        
        # 其他参数设置
        self.create_other_params_section(scrollable_frame)
        
        # 分隔线
        ttk.Separator(scrollable_frame, orient='horizontal').pack(fill=tk.X, pady=15)
        
        # 外观设置
        self.create_appearance_section(scrollable_frame)
    
    def create_simion_path_section(self, parent):
        """创建 SIMION 路径设置区域"""
        path_frame = tk.LabelFrame(
            parent,
            text="文件路径配置",
            font=(FONT_FAMILY, FONT_SIZE_BOLD, "bold"),
            bg=WINDOW_BG_COLOR,
            fg=COLORS['text'],
            padx=15,
            pady=10
        )
        path_frame.pack(fill=tk.X, pady=(0, 10))
        
        # SIMION 可执行文件路径
        tk.Label(
            path_frame,
            text="SIMION 可执行文件:",
            font=(FONT_FAMILY, FONT_SIZE_NORMAL, "bold"),
            bg=WINDOW_BG_COLOR
        ).pack(anchor=tk.W, pady=(0, 5))
        
        simion_input_frame = tk.Frame(path_frame, bg=WINDOW_BG_COLOR)
        simion_input_frame.pack(fill=tk.X, pady=(0, 5))
        
        self.path_entry = tk.Entry(
            simion_input_frame,
            textvariable=self.simion_path_var,
            font=(FONT_FAMILY, FONT_SIZE_NORMAL),
            width=40
        )
        self.path_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 5))
        
        browse_simion_btn = tk.Button(
            simion_input_frame,
            text="浏览",
            command=self.browse_simion_path,
            font=(FONT_FAMILY, FONT_SIZE_NORMAL),
            bg=COLORS['secondary'],
            fg='white',
            cursor='hand2',
            relief=tk.FLAT,
            padx=15,
            pady=5
        )
        browse_simion_btn.pack(side=tk.LEFT)
        
        # SIMION 提示信息
        hint_label = tk.Label(
            path_frame,
            text="通常位于: D:\\SIMION-2024\\simion.exe",
            font=(FONT_FAMILY, FONT_SIZE_NORMAL - 1),
            bg=WINDOW_BG_COLOR,
            fg=COLORS['info']
        )
        hint_label.pack(anchor=tk.W, pady=(0, 10))
        
        # IOB 文件路径
        tk.Label(
            path_frame,
            text="IOB 文件路径:",
            font=(FONT_FAMILY, FONT_SIZE_NORMAL, "bold"),
            bg=WINDOW_BG_COLOR
        ).pack(anchor=tk.W, pady=(5, 5))
        
        iob_input_frame = tk.Frame(path_frame, bg=WINDOW_BG_COLOR)
        iob_input_frame.pack(fill=tk.X, pady=(0, 5))
        
        self.iob_path_entry = tk.Entry(
            iob_input_frame,
            textvariable=self.iob_path_var,
            font=(FONT_FAMILY, FONT_SIZE_NORMAL),
            width=40
        )
        self.iob_path_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 5))
        
        browse_iob_btn = tk.Button(
            iob_input_frame,
            text="浏览",
            command=self.browse_iob_path,
            font=(FONT_FAMILY, FONT_SIZE_NORMAL),
            bg=COLORS['secondary'],
            fg='white',
            cursor='hand2',
            relief=tk.FLAT,
            padx=15,
            pady=5
        )
        browse_iob_btn.pack(side=tk.LEFT)
        
        # Lua 文件路径
        tk.Label(
            path_frame,
            text="Lua 文件路径:",
            font=(FONT_FAMILY, FONT_SIZE_NORMAL, "bold"),
            bg=WINDOW_BG_COLOR
        ).pack(anchor=tk.W, pady=(5, 5))
        
        lua_input_frame = tk.Frame(path_frame, bg=WINDOW_BG_COLOR)
        lua_input_frame.pack(fill=tk.X, pady=(0, 5))
        
        self.lua_path_entry = tk.Entry(
            lua_input_frame,
            textvariable=self.lua_path_var,
            font=(FONT_FAMILY, FONT_SIZE_NORMAL),
            width=40
        )
        self.lua_path_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 5))
        
        browse_lua_btn = tk.Button(
            lua_input_frame,
            text="浏览",
            command=self.browse_lua_path,
            font=(FONT_FAMILY, FONT_SIZE_NORMAL),
            bg=COLORS['secondary'],
            fg='white',
            cursor='hand2',
            relief=tk.FLAT,
            padx=15,
            pady=5
        )
        browse_lua_btn.pack(side=tk.LEFT)
        
        # CSV 保存路径
        tk.Label(
            path_frame,
            text="CSV 保存文件夹:",
            font=(FONT_FAMILY, FONT_SIZE_NORMAL, "bold"),
            bg=WINDOW_BG_COLOR
        ).pack(anchor=tk.W, pady=(5, 5))
        
        csv_input_frame = tk.Frame(path_frame, bg=WINDOW_BG_COLOR)
        csv_input_frame.pack(fill=tk.X, pady=(0, 5))
        
        self.csv_save_path_entry = tk.Entry(
            csv_input_frame,
            textvariable=self.csv_save_path_var,
            font=(FONT_FAMILY, FONT_SIZE_NORMAL),
            width=40
        )
        self.csv_save_path_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 5))
        
        browse_csv_btn = tk.Button(
            csv_input_frame,
            text="浏览",
            command=self.browse_csv_save_path,
            font=(FONT_FAMILY, FONT_SIZE_NORMAL),
            bg=COLORS['secondary'],
            fg='white',
            cursor='hand2',
            relief=tk.FLAT,
            padx=15,
            pady=5
        )
        browse_csv_btn.pack(side=tk.LEFT)
        
        # 工作空间保留设置
        workspace_frame = tk.Frame(path_frame, bg=WINDOW_BG_COLOR)
        workspace_frame.pack(fill=tk.X, pady=(10, 0))
        
        # 工作空间保留开关
        self.keep_workspace_var = tk.BooleanVar(value=params_store.get('keep_workspace', False))
        workspace_checkbox = tk.Checkbutton(
            workspace_frame,
            text="保留多线程 SIMION 工作空间",
            variable=self.keep_workspace_var,
            font=(FONT_FAMILY, FONT_SIZE_NORMAL),
            bg=WINDOW_BG_COLOR,
            fg=COLORS['text'],
            selectcolor=COLORS['primary'],
            activebackground=WINDOW_BG_COLOR,
            activeforeground=COLORS['text']
        )
        workspace_checkbox.pack(anchor=tk.W)
        
        # 工作空间说明
        workspace_hint = tk.Label(
            workspace_frame,
            text="开启后保留 simion_workspace 目录结构，用于调试多线程问题",
            font=(FONT_FAMILY, FONT_SIZE_NORMAL - 1),
            bg=WINDOW_BG_COLOR,
            fg=COLORS['info']
        )
        workspace_hint.pack(anchor=tk.W, pady=(2, 0))
    
    def create_rf_params_section(self, parent):
        """创建 RF 参数设置区域"""
        rf_frame = tk.LabelFrame(
            parent,
            text="RF 电压参数",
            font=(FONT_FAMILY, FONT_SIZE_BOLD, "bold"),
            bg=WINDOW_BG_COLOR,
            fg=COLORS['text'],
            padx=15,
            pady=10
        )
        rf_frame.pack(fill=tk.X, pady=(0, 10))
        
        # _rfvolt 参数
        self.create_param_input(rf_frame, "_rfvolt (V):", self.rfvolt_var, "伏特", field_name="rfvolt")
        
        # _rfvolt2 参数
        self.create_param_input(rf_frame, "_rfvolt2 (V):", self.rfvolt2_var, "伏特", field_name="rfvolt2") 
    
    def create_frequency_section(self, parent):
        """创建频率参数设置区域"""
        freq_frame = tk.LabelFrame(
            parent,
            text="频率扫描参数",
            font=(FONT_FAMILY, FONT_SIZE_BOLD, "bold"),
            bg=WINDOW_BG_COLOR,
            fg=COLORS['text'],
            padx=15,
            pady=10
        )
        freq_frame.pack(fill=tk.X, pady=(0, 10))
        
        # 频率参数标题
        tk.Label(
            freq_frame,
            text="扫描范围设置：",
            font=(FONT_FAMILY, FONT_SIZE_NORMAL, "bold"),
            bg=WINDOW_BG_COLOR,
            fg=COLORS['text']
        ).pack(anchor=tk.W, pady=(0, 5))
        
        # 起始频率
        self.create_param_input(freq_frame, "_frequency_start (Hz):", self.freq_start_var, "赫兹", width=20, field_name="freq_start")
        
        # 结束频率
        self.create_param_input(freq_frame, "_frequency_end (Hz):", self.freq_end_var, "赫兹", width=20, field_name="freq_end")
        
        # 步长
        self.create_param_input(freq_frame, "_frequency_step (Hz):", self.freq_step_var, "赫兹", width=20, field_name="freq_step")
        
        # 提示信息框
        hint_frame = tk.Frame(freq_frame, bg='#e8f4f8', relief=tk.SOLID, borderwidth=1)
        hint_frame.pack(fill=tk.X, pady=(10, 0), padx=2)
        
        hint_label = tk.Label(
            hint_frame,
            text="💡 说明：\n"
                 "• frequency_start: 频率扫描起始值\n"
                 "• frequency_end: 频率扫描结束值\n"
                 "• frequency_step: 频率递增步长\n",
            font=(FONT_FAMILY, FONT_SIZE_NORMAL - 1),
            bg='#e8f4f8',
            fg='#004085',
            justify=tk.LEFT,
            padx=10,
            pady=8
        )
        hint_label.pack(anchor=tk.W)
    
    def create_other_params_section(self, parent):
        """创建其他参数设置区域"""
        other_frame = tk.LabelFrame(
            parent,
            text="粒子参数",
            font=(FONT_FAMILY, FONT_SIZE_BOLD, "bold"),
            bg=WINDOW_BG_COLOR,
            fg=COLORS['text'],
            padx=15,
            pady=10
        )
        other_frame.pack(fill=tk.X, pady=(0, 10))
        
        # _ke 加速电压
        self.create_param_input(other_frame, "_ke 加速电压 (V):", self.ke_var, "伏特", field_name="ke")
        
        # _length 长度
        self.create_param_input(other_frame, "_length 长度 (mm):", self.length_var, "毫米", field_name="length")
        
        # 线程数量
        self.create_param_input(other_frame, "线程数量:", self.thread_count_var, "个", field_name="thread_count")
        
        # 线程数量说明
        thread_hint_frame = tk.Frame(other_frame, bg='#fff3cd', relief=tk.SOLID, borderwidth=1)
        thread_hint_frame.pack(fill=tk.X, pady=(10, 0), padx=2)
        
        thread_hint_label = tk.Label(
            thread_hint_frame,
            text="💡 说明：\n"
                 "• 线程数量：并行处理的频率数量\n"
                 "• 过多线程可能导致系统负载过高",
            font=(FONT_FAMILY, FONT_SIZE_NORMAL - 1),
            bg='#fff3cd',
            fg='#856404',
            justify=tk.LEFT,
            padx=10,
            pady=8
        )
        thread_hint_label.pack(anchor=tk.W)
    
    def create_param_input(self, parent, label_text, var, unit_text, width=20, field_name=None):
        """创建参数输入框（通用方法）"""
        param_frame = tk.Frame(parent, bg=WINDOW_BG_COLOR)
        param_frame.pack(fill=tk.X, pady=5)
        
        # 标签
        label = tk.Label(
            param_frame,
            text=label_text,
            font=(FONT_FAMILY, FONT_SIZE_NORMAL),
            bg=WINDOW_BG_COLOR,
            width=18,
            anchor=tk.W
        )
        label.pack(side=tk.LEFT)
        
        # 输入框
        param_entry = tk.Entry(
            param_frame,
            textvariable=var,
            font=(FONT_FAMILY, FONT_SIZE_NORMAL),
            width=width
        )
        param_entry.pack(side=tk.LEFT, padx=5)
        
        # 单位标签
        unit_label = tk.Label(
            param_frame,
            text=unit_text,
            font=(FONT_FAMILY, FONT_SIZE_NORMAL),
            bg=WINDOW_BG_COLOR,
            fg=COLORS['secondary']
        )
        unit_label.pack(side=tk.LEFT)
        
        # 存储输入框引用以便错误标记
        if field_name:
            setattr(self, f'{field_name}_entry', param_entry)
        
        return param_entry
    
    def create_appearance_section(self, parent):
        """创建外观设置区域"""
        appearance_frame = tk.LabelFrame(
            parent,
            text="🎨 界面外观设置",
            font=(FONT_FAMILY, FONT_SIZE_BOLD, "bold"),
            bg=WINDOW_BG_COLOR,
            fg=COLORS['text'],
            padx=15,
            pady=10
        )
        appearance_frame.pack(fill=tk.X, pady=(0, 10))
        
        # 主题设置
        theme_frame = tk.Frame(appearance_frame, bg=WINDOW_BG_COLOR)
        theme_frame.pack(fill=tk.X, pady=(0, 10))
        
        tk.Label(
            theme_frame,
            text="主题:",
            font=(FONT_FAMILY, FONT_SIZE_NORMAL, "bold"),
            bg=WINDOW_BG_COLOR
        ).pack(side=tk.LEFT, padx=(0, 10))
        
        # 主题下拉菜单
        theme_options = [
            'cosmo', 'journal', 'darkly', 'flatly', 'litera', 
            'lumen', 'minty', 'pulse', 'sandstone', 'united', 'yeti'
        ]
        
        # 使用准确的小部件来源以避免命名空间混淆
        if BOOTSTRAP_AVAILABLE:
            from ttkbootstrap.widgets import Combobox as BTCombobox
            theme_combo = BTCombobox(
                theme_frame,
                textvariable=self.appearance_theme_var,
                values=theme_options,
                state="readonly",
                width=15
            )
        else:
            theme_combo = ttk.Combobox(
                theme_frame,
                textvariable=self.appearance_theme_var,
                values=theme_options,
                state="readonly",
                width=15
            )
        theme_combo.pack(side=tk.LEFT, padx=(0, 10))
        
        # 字体大小设置
        font_frame = tk.Frame(appearance_frame, bg=WINDOW_BG_COLOR)
        font_frame.pack(fill=tk.X, pady=(0, 10))
        
        tk.Label(
            font_frame,
            text="字体大小:",
            font=(FONT_FAMILY, FONT_SIZE_NORMAL, "bold"),
            bg=WINDOW_BG_COLOR
        ).pack(side=tk.LEFT, padx=(0, 10))
        
        # 字体大小滑动条
        font_scale = tk.Scale(
            font_frame,
            from_=8,
            to=16,
            orient=tk.HORIZONTAL,
            variable=self.appearance_font_size_var,
            bg=WINDOW_BG_COLOR,
            highlightthickness=0,
            length=200
        )
        font_scale.pack(side=tk.LEFT, padx=(0, 10))
        
        # 字体大小显示标签
        font_size_label = tk.Label(
            font_frame,
            textvariable=self.appearance_font_size_var,
            font=(FONT_FAMILY, FONT_SIZE_NORMAL),
            bg=WINDOW_BG_COLOR,
            fg=COLORS['secondary']
        )
        font_size_label.pack(side=tk.LEFT)
        
        # 窗口设置
        window_frame = tk.Frame(appearance_frame, bg=WINDOW_BG_COLOR)
        window_frame.pack(fill=tk.X, pady=(0, 10))
        
        # 窗口大小设置
        size_frame = tk.Frame(window_frame, bg=WINDOW_BG_COLOR)
        size_frame.pack(fill=tk.X, pady=(0, 5))
        
        tk.Label(
            size_frame,
            text="窗口大小:",
            font=(FONT_FAMILY, FONT_SIZE_NORMAL, "bold"),
            bg=WINDOW_BG_COLOR
        ).pack(side=tk.LEFT, padx=(0, 10))
        
        # 宽度设置
        tk.Label(
            size_frame,
            text="宽度:",
            font=(FONT_FAMILY, FONT_SIZE_NORMAL),
            bg=WINDOW_BG_COLOR
        ).pack(side=tk.LEFT, padx=(0, 5))
        
        width_spinbox = tk.Spinbox(
            size_frame,
            from_=800,
            to=1920,
            textvariable=self.appearance_window_width_var,
            width=8,
            font=(FONT_FAMILY, FONT_SIZE_NORMAL)
        )
        width_spinbox.pack(side=tk.LEFT, padx=(0, 10))
        
        # 高度设置
        tk.Label(
            size_frame,
            text="高度:",
            font=(FONT_FAMILY, FONT_SIZE_NORMAL),
            bg=WINDOW_BG_COLOR
        ).pack(side=tk.LEFT, padx=(0, 5))
        
        height_spinbox = tk.Spinbox(
            size_frame,
            from_=600,
            to=1080,
            textvariable=self.appearance_window_height_var,
            width=8,
            font=(FONT_FAMILY, FONT_SIZE_NORMAL)
        )
        height_spinbox.pack(side=tk.LEFT, padx=(0, 10))
        
        # 窗口选项
        options_frame = tk.Frame(window_frame, bg=WINDOW_BG_COLOR)
        options_frame.pack(fill=tk.X)
        
        # 允许调整窗口大小
        resizable_check = tk.Checkbutton(
            options_frame,
            text="允许调整窗口大小",
            variable=self.appearance_window_resizable_var,
            font=(FONT_FAMILY, FONT_SIZE_NORMAL),
            bg=WINDOW_BG_COLOR,
            activebackground=WINDOW_BG_COLOR
        )
        resizable_check.pack(side=tk.LEFT, padx=(0, 20))
        
        # 记住窗口大小
        remember_size_check = tk.Checkbutton(
            options_frame,
            text="记住窗口大小",
            variable=self.appearance_window_remember_size_var,
            font=(FONT_FAMILY, FONT_SIZE_NORMAL),
            bg=WINDOW_BG_COLOR,
            activebackground=WINDOW_BG_COLOR
        )
        remember_size_check.pack(side=tk.LEFT)
    
    def create_button_section(self):
        """创建按钮区域（固定在底部）"""
        # 创建底部按钮框架
        if BOOTSTRAP_AVAILABLE:
            button_container = ttk.Frame(self.window)
        else:
            button_container = tk.Frame(
                self.window, 
                bg='#e0e0e0',
                relief=tk.RAISED, 
                borderwidth=1
            )
        button_container.pack(side=tk.BOTTOM, fill=tk.X, padx=0, pady=0)
        
        if BOOTSTRAP_AVAILABLE:
            button_frame = ttk.Frame(button_container, padding="15")
        else:
            button_frame = tk.Frame(button_container, bg='#e0e0e0')
        button_frame.pack(pady=15)
        
        if BOOTSTRAP_AVAILABLE:
            # 创建图标（保存为实例属性以防止被垃圾回收）
            self.save_icon = get_icon_info()
            self.cancel_icon = get_icon_error()
            
            # 保存按钮
            save_btn = ttk.Button(
                button_frame,
                text="保存设置",
                image=self.save_icon if self.save_icon else '',
                command=self.save_settings,
                bootstyle=SUCCESS,
                width=15,
                compound=tk.LEFT if self.save_icon else tk.NONE
            )
            save_btn.pack(side=tk.LEFT, padx=8)
            
            # 取消按钮
            cancel_btn = ttk.Button(
                button_frame,
                text="取消",
                image=self.cancel_icon if self.cancel_icon else '',
                command=self.on_cancel,
                bootstyle=SECONDARY,
                width=15,
                compound=tk.LEFT if self.cancel_icon else tk.NONE
            )
            cancel_btn.pack(side=tk.LEFT, padx=8)
        else:
            # 保存按钮
            save_btn = tk.Button(
                button_frame,
                text="保存设置",
                command=self.save_settings,
                font=(FONT_FAMILY, FONT_SIZE_NORMAL, "bold"),
                bg=COLORS['success'],
                fg='white',
                cursor='hand2',
                relief=tk.RAISED,
                padx=25,
                pady=10,
                width=10
            )
            save_btn.pack(side=tk.LEFT, padx=8)
            
            # 取消按钮
            cancel_btn = tk.Button(
                button_frame,
                text="取消",
                command=self.on_cancel,
                font=(FONT_FAMILY, FONT_SIZE_NORMAL),
                bg=COLORS['secondary'],
                fg='white',
                cursor='hand2',
                relief=tk.RAISED,
                padx=25,
                pady=10,
                width=10
            )
            cancel_btn.pack(side=tk.LEFT, padx=8)
    
    def browse_simion_path(self):
        """浏览并选择 SIMION 可执行文件"""
        filename = filedialog.askopenfilename(
            title="选择 SIMION 可执行文件",
            filetypes=[
                ("可执行文件", "*.exe"),
                ("所有文件", "*.*")
            ],
            initialdir="D:\\"
        )
        
        if filename:
            self.simion_path_var.set(filename)
    
    def browse_iob_path(self):
        """浏览并选择 IOB 文件"""
        filename = filedialog.askopenfilename(
            title="选择 IOB 文件",
            filetypes=[
                ("IOB 文件", "*.iob"),
                ("所有文件", "*.*")
            ],
            initialdir=os.path.dirname(self.iob_path_var.get()) if self.iob_path_var.get() else ""
        )
        
        if filename:
            self.iob_path_var.set(filename)
    
    def browse_lua_path(self):
        """浏览并选择 Lua 文件"""
        filename = filedialog.askopenfilename(
            title="选择 Lua 文件",
            filetypes=[
                ("Lua 文件", "*.lua"),
                ("所有文件", "*.*")
            ],
            initialdir=os.path.dirname(self.lua_path_var.get()) if self.lua_path_var.get() else ""
        )
        
        if filename:
            self.lua_path_var.set(filename)
    
    def browse_csv_save_path(self):
        """浏览并选择 CSV 保存文件夹"""
        folder_path = filedialog.askdirectory(
            title="选择 CSV 保存文件夹",
            initialdir=os.path.dirname(self.csv_save_path_var.get()) if self.csv_save_path_var.get() else ""
        )
        
        if folder_path:
            self.csv_save_path_var.set(folder_path)
    
    def validate_inputs(self):
        """验证输入参数"""
        try:
            # 清除之前的错误标记
            self.clear_error_marks()
            
            # 收集所有错误信息
            errors = []
            empty_fields = []
            
            # 验证 SIMION 路径（必填）
            simion_path = self.simion_path_var.get().strip()
            if not simion_path:
                errors.append("SIMION 可执行文件路径")
                empty_fields.append('simion_path')
            elif not os.path.exists(simion_path):
                result = messagebox.askyesno(
                    "警告",
                    f"文件不存在:\n{simion_path}\n\n是否仍要保存此路径？"
                )
                if not result:
                    return False
            
            # 验证 IOB 路径（必填）
            iob_path = self.iob_path_var.get().strip()
            if not iob_path:
                errors.append("IOB 文件路径")
                empty_fields.append('iob_path')
            elif not os.path.exists(iob_path):
                result = messagebox.askyesno(
                    "警告",
                    f"IOB 文件不存在:\n{iob_path}\n\n是否仍要保存此路径？"
                )
                if not result:
                    return False
            
            # 验证 Lua 路径（必填）
            lua_path = self.lua_path_var.get().strip()
            if not lua_path:
                errors.append("Lua 文件路径")
                empty_fields.append('lua_path')
            elif not os.path.exists(lua_path):
                result = messagebox.askyesno(
                    "警告",
                    f"Lua 文件不存在:\n{lua_path}\n\n是否仍要保存此路径？"
                )
                if not result:
                    return False
            
            # 验证 CSV 保存路径（必填）
            csv_save_path = self.csv_save_path_var.get().strip()
            if not csv_save_path:
                errors.append("CSV 保存文件夹")
                empty_fields.append('csv_save_path')
            
            # 验证 RF 电压（必填）
            try:
                rfvolt = self.rfvolt_var.get()
                if rfvolt is None or rfvolt == 0:
                    errors.append("RF 电压 1 (_rfvolt)")
                    empty_fields.append('rfvolt')
            except tk.TclError:
                errors.append("RF 电压 1 (_rfvolt)")
                empty_fields.append('rfvolt')
            
            try:
                rfvolt2 = self.rfvolt2_var.get()
                if rfvolt2 is None or rfvolt2 == 0:
                    errors.append("RF 电压 2 (_rfvolt2)")
                    empty_fields.append('rfvolt2')
            except tk.TclError:
                errors.append("RF 电压 2 (_rfvolt2)")
                empty_fields.append('rfvolt2')
            
            # 验证频率参数（必填）
            freq_start_str = self.freq_start_var.get().strip()
            if not freq_start_str:
                errors.append("起始频率 (_frequency_start)")
                empty_fields.append('freq_start')
            else:
                try:
                    freq_start = float(freq_start_str)
                    if freq_start < 0 or freq_start > 1000000000:
                        messagebox.showerror("错误", "_frequency_start 值应在 0-1,000,000,000 Hz 范围内")
                        return False
                except ValueError:
                    errors.append("起始频率 (_frequency_start)")
                    empty_fields.append('freq_start')
            
            freq_end_str = self.freq_end_var.get().strip()
            if not freq_end_str:
                errors.append("结束频率 (_frequency_end)")
                empty_fields.append('freq_end')
            else:
                try:
                    freq_end = float(freq_end_str)
                    if freq_end < 0 or freq_end > 1000000000:
                        messagebox.showerror("错误", "_frequency_end 值应在 0-1,000,000,000 Hz 范围内")
                        return False
                except ValueError:
                    errors.append("结束频率 (_frequency_end)")
                    empty_fields.append('freq_end')
            
            # 验证频率范围逻辑（允许起始和结束频率相同）
            if freq_start_str and freq_end_str:
                try:
                    start_val = float(freq_start_str)
                    end_val = float(freq_end_str)
                    if start_val > end_val:
                        messagebox.showerror("错误", "起始频率不能大于结束频率")
                        return False
                except ValueError:
                    pass  # 已经在上面处理了
            
            # 验证加速电压（检查是否为空值或零值）
            try:
                ke = self.ke_var.get()
                if ke is None or ke == 0:
                    errors.append("加速电压 (_ke)")
                    empty_fields.append('ke')
            except tk.TclError:
                errors.append("加速电压 (_ke)")
                empty_fields.append('ke')
            
            # 验证长度（检查是否为空值或零值）
            try:
                length = self.length_var.get()
                if length is None or length == 0:
                    errors.append("长度 (_length)")
                    empty_fields.append('length')
            except tk.TclError:
                errors.append("长度 (_length)")
                empty_fields.append('length')
            
            
            # 如果有错误，显示错误信息并标记空字段
            if errors:
                error_msg = "以下参数为空，保存失败：\n\n" + "\n".join(f"• {error}" for error in errors)
                messagebox.showerror("参数验证失败", error_msg)
                self.mark_empty_fields(empty_fields)
                return False
            
            return True
            
        except Exception as e:
            messagebox.showerror("错误", f"参数验证时发生错误: {str(e)}")
            return False
    
    def mark_empty_fields(self, empty_fields):
        """标记空的输入字段"""
        # 存储错误标记的引用
        if not hasattr(self, 'error_marks'):
            self.error_marks = {}
        
        # 字段映射到输入框
        field_mapping = {
            'simion_path': getattr(self, 'path_entry', None),
            'iob_path': getattr(self, 'iob_path_entry', None),
            'lua_path': getattr(self, 'lua_path_entry', None),
            'csv_save_path': getattr(self, 'csv_save_path_entry', None),
            'rfvolt': getattr(self, 'rfvolt_entry', None),
            'rfvolt2': getattr(self, 'rfvolt2_entry', None),
            'freq_start': getattr(self, 'freq_start_entry', None),
            'freq_end': getattr(self, 'freq_end_entry', None),
            'ke': getattr(self, 'ke_entry', None),
            'length': getattr(self, 'length_entry', None)
        }
        
        # 为每个空字段添加红色星号标记
        for field in empty_fields:
            entry_widget = field_mapping.get(field)
            if entry_widget:
                self.add_error_mark(entry_widget, field)
    
    def add_error_mark(self, widget, field_name):
        """为输入框添加错误标记"""
        try:
            # 只设置输入框背景色为浅红色
            widget.config(bg='#ffebee')
            
            # 保存错误标记的引用（用于清除时恢复背景色）
            if not hasattr(self, 'error_marks'):
                self.error_marks = {}
            self.error_marks[field_name] = widget
            
        except Exception as e:
            logger.error(f"添加错误标记失败: {e}")
    
    def clear_error_marks(self):
        """清除所有错误标记"""
        try:
            if hasattr(self, 'error_marks'):
                # 恢复所有标记过的输入框的背景色
                for field_name, widget in self.error_marks.items():
                    if widget and widget.winfo_exists():
                        widget.config(bg='white')
                self.error_marks.clear()
                    
        except Exception as e:
            logger.error(f"清除错误标记失败: {e}")
    
    def save_settings(self):
        """保存设置到 params_store"""
        if not self.validate_inputs():
            return
        
        try:
            # 处理频率参数
            freq_start_str = self.freq_start_var.get().strip()
            freq_start = None if not freq_start_str else float(freq_start_str)
            
            freq_end_str = self.freq_end_var.get().strip()
            freq_end = None if not freq_end_str else float(freq_end_str)
            
            # 更新 params_store
            params_store.update({
                'simion_path': self.simion_path_var.get(),
                'iob_path': self.iob_path_var.get().strip(),
                'lua_path': self.lua_path_var.get().strip(),
                'csv_save_path': self.csv_save_path_var.get().strip(),
                '_rfvolt': self.rfvolt_var.get(),
                '_rfvolt2': self.rfvolt2_var.get(),
                '_frequency_start': freq_start,
                '_frequency_end': freq_end,
                '_frequency_step': self.freq_step_var.get(),
                '_ke': self.ke_var.get(),
                '_length': self.length_var.get(),
                'thread_count': self.thread_count_var.get(),
                'keep_workspace': self.keep_workspace_var.get(),
                # 外观设置
                'appearance_theme': self.appearance_theme_var.get(),
                'appearance_font_size': self.appearance_font_size_var.get(),
                'appearance_window_width': self.appearance_window_width_var.get(),
                'appearance_window_height': self.appearance_window_height_var.get(),
                'appearance_window_resizable': self.appearance_window_resizable_var.get(),
                'appearance_window_remember_size': self.appearance_window_remember_size_var.get()
            })
            
            # 生成频率列表
            frequency_list = params_store.generate_frequency_list()
            
            # 持久化保存
            if params_store.save():
                # 构建成功消息
                success_msg = "参数保存成功！\n\n"
                success_msg += "下次启动时将自动加载这些参数。"
                
                # 输出参数到日志
                self.log_saved_parameters()
                
                messagebox.showinfo("成功", success_msg)
                # 清理鼠标滚轮事件绑定
                if hasattr(self, 'main_canvas') and self.main_canvas:
                    try:
                        self.main_canvas.unbind_all("<MouseWheel>")
                    except:
                        pass
                self.window.destroy()
            else:
                messagebox.showerror("错误", "保存参数失败")
        
        except Exception as e:
            messagebox.showerror("错误", f"保存参数时发生错误: {str(e)}")
    
    def log_saved_parameters(self):
        """将保存的参数输出到日志"""
        try:
            from utils.logger import logger
            
            logger.info("=== 参数设置已保存 ===")
            logger.info(f"SIMION 路径: {self.simion_path_var.get()}")
            logger.info(f"IOB 路径: {self.iob_path_var.get() or '未设置'}")
            logger.info(f"Lua 路径: {self.lua_path_var.get() or '未设置'}")
            logger.info(f"CSV 保存文件夹: {self.csv_save_path_var.get() or '未设置'}")
            logger.info(f"RF 电压 1: {self.rfvolt_var.get()} V")
            logger.info(f"RF 电压 2: {self.rfvolt2_var.get()} V")
            
            # 频率参数
            freq_start = self.freq_start_var.get().strip()
            freq_end = self.freq_end_var.get().strip()
            logger.info(f"频率起始: {freq_start if freq_start else '未设置'} Hz")
            logger.info(f"频率结束: {freq_end if freq_end else '未设置'} Hz")
            logger.info(f"频率步长: {self.freq_step_var.get()} Hz")
            
            logger.info(f"加速电压: {self.ke_var.get()} V")
            logger.info(f"长度: {self.length_var.get()} mm")
            logger.info(f"线程数量: {self.thread_count_var.get()} 个")
            logger.info(f"保留工作空间: {'是' if self.keep_workspace_var.get() else '否'}")
            
            # 外观设置
            logger.info(f"界面主题: {self.appearance_theme_var.get()}")
            logger.info(f"字体大小: {self.appearance_font_size_var.get()}")
            logger.info(f"窗口大小: {self.appearance_window_width_var.get()}x{self.appearance_window_height_var.get()}")
            logger.info(f"允许调整窗口: {'是' if self.appearance_window_resizable_var.get() else '否'}")
            logger.info(f"记住窗口大小: {'是' if self.appearance_window_remember_size_var.get() else '否'}")
            
            # 获取并显示频率列表
            frequency_list = params_store.get('_frequency_list', [])
            if frequency_list:
                freq_display = [int(f) for f in frequency_list[:10]]
                if len(frequency_list) > 10:
                    logger.info(f"频率列表 ({len(frequency_list)} 个值): {freq_display}...")
                else:
                    logger.info(f"频率列表 ({len(frequency_list)} 个值): {freq_display}")
            else:
                logger.info("频率列表: 空（未设置起始/结束频率）")
            
            logger.info("========================")
            
        except Exception as e:
            logger.error(f"输出参数到日志失败: {e}")
    
    
    def on_cancel(self):
        """取消并关闭窗口（不保存）"""
        result = messagebox.askyesno(
            "确认",
            "是否放弃当前修改并关闭窗口？"
        )
        if result:
            # 清理鼠标滚轮事件绑定
            if hasattr(self, 'main_canvas') and self.main_canvas:
                try:
                    self.main_canvas.unbind_all("<MouseWheel>")
                except:
                    pass
            self.window.destroy()
    
    def show(self):
        """显示窗口（阻塞式）"""
        self.window.grab_set()
        self.window.wait_window()


def open_settings_window(parent=None):
    """
    打开参数设置窗口（便捷函数）
    
    参数:
        parent: 父窗口（可选）
    
    返回:
        设置窗口实例
    """
    window = SettingsWindow(parent)
    return window


# ==================== 测试代码 ====================

if __name__ == '__main__':
    # 测试参数设置窗口
    window = SettingsWindow()
    window.window.mainloop()
